package com.clusterswisdom.horn.api.tmall;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.ServletContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.clusterswisdom.horn.api.exception.HornApiException;
import com.clusterswisdom.horn.api.ioc.HornPropertyParser;
import com.clusterswisdom.horn.api.ioc.api.ApiConfig;
import com.clusterswisdom.horn.api.ioc.api.HornConfig;
import com.clusterswisdom.horn.api.ioc.api.ParamType;
import com.clusterswisdom.horn.api.ioc.api.RequestParam;
import com.clusterswisdom.horn.api.ioc.struct.SourceField;
import com.clusterswisdom.horn.api.ioc.struct.StructConfig;
import com.clusterswisdom.horn.api.ioc.struct.StructContainer;
import com.clusterswisdom.horn.api.tmall.client.TmClient;
import com.clusterswisdom.horn.api.tmall.config.TmallApplication;
import com.clusterswisdom.horn.api.tmall.config.TmallConfig;
import com.clusterswisdom.horn.api.util.ParamUtil;
import com.taobao.api.TaobaoRequest;
import com.taobao.api.TaobaoResponse;

/**
 * 
 * 天猫整体类设置
 * 
 * @author bin
 *
 */
public class TmallInstance {
	
	private TmallConfig config;
	
	private Map<String, TmClient> clientMap = new HashMap<String, TmClient>();
	
	private static TmallInstance instance = new TmallInstance();
	
	private Logger log = LoggerFactory.getLogger(TmallInstance.class);
	
	public static TmallInstance getInstance() {
		return instance;
	}
	
	@SuppressWarnings("unchecked")
	public void init(ServletContext sc) {
		config = new TmallConfig(sc);
		HornConfig.getConfig().setChannelParamParser(config.getChannel(), new TmallPropertyParser());
		
		Iterator<TmallApplication> entryIterator = config.getTmallApplications().values().iterator();
		while (entryIterator.hasNext()) {
			TmallApplication application = entryIterator.next();
			if (application == null || application.getClassName() == null) {
				continue;
			}
			
			try {
				Class<? extends TmClient> clientClass = (Class<? extends TmClient>)Thread.currentThread().getContextClassLoader().loadClass(application.getClassName());
			    Constructor<? extends TmClient> constructor = clientClass.getConstructor(TmallApplication.class);
				
				TmClient client =  constructor.newInstance(application);
				clientMap.put(application.getName(), client);
				
			} catch (Exception e) {
				e.printStackTrace();
				
			}
			
		}
	}

	public TmallConfig getConfig() {
		return config;
	}
	
	public TmClient getClient(String application) {
		if (application == null) {
			application = config.getDefaultApplication().getName();
		}
		
		return clientMap.get(application);
	}
	
	public TaobaoResponse executeRequest(Map<String, Object> param, ApiConfig apiConfig) throws Exception {
		TaobaoRequest<TaobaoResponse> request = createTaobaoRequest(param, apiConfig);
		return invokeApiConfig(apiConfig, request);
	}
	
	public TaobaoResponse executeRequest(Map<String, Object> param, String service, String api) throws Exception {
		ApiConfig apiConfig = HornConfig.getConfig().getApiConfig(config.getChannel(), service, api);
		TaobaoRequest<TaobaoResponse> request = createTaobaoRequest(param, apiConfig);
		return invokeApiConfig(apiConfig, request);
	}
	
	private TaobaoResponse invokeApiConfig(ApiConfig apiConfig, TaobaoRequest<TaobaoResponse> request) throws HornApiException {
		String application = apiConfig.getApplication();
		if (application == null) {
			application = config.getDefaultApplication().getName();
		}
		
		TmallApplication applicationConfig = config.getApplication(application);
		TmClient client = clientMap.get(application);
		if (client != null) {
			try {
				if ("true".equalsIgnoreCase(apiConfig.getAttribute("needToken"))) {
					log.debug("invoke " + request.getTextParams().toString());
					return client.execute(request, applicationConfig.getSessionKey());
					
				} else {
					return client.execute(request);
					
				}
				
			} catch (Exception e) {
				e.printStackTrace();
				throw new HornApiException(e.getMessage());
				
			}
			
		} else {
			return null;
			
		}
	}
	
	@SuppressWarnings("unchecked")
	private TaobaoRequest<TaobaoResponse> createTaobaoRequest(Map<String, Object> param, ApiConfig config) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
		String requestClassName = config.getAttribute("requestClass");
		log.debug("requestClass :" + requestClassName);
		Class<? extends TaobaoRequest<TaobaoResponse>> requestClass = (Class<? extends TaobaoRequest<TaobaoResponse>>)Thread.currentThread().getContextClassLoader().loadClass(requestClassName);

		TaobaoRequest<TaobaoResponse> request = requestClass.newInstance();
		for (RequestParam requestParam : config.getRequest().getParams()) {
			Object paramValue = getParamValue(param, requestParam);
			if (paramValue == null) {
				continue;
			}
			
			Method method = requestClass.getMethod(getMethodName(requestParam), paramValue.getClass());
			log.debug("set param " + method.getName() + ":" + paramValue);
			method.invoke(request, paramValue);
		}
		
		return request;
	}
	
	private String getMethodName(RequestParam requestParam) {
		String propertyName = requestParam.getMethod();
		if (propertyName == null) {
			String paramName = requestParam.getMapping();
			if (paramName == null) {
				paramName = requestParam.getName();
			}
			
			propertyName = getMethodNameFromParamName(paramName);
			requestParam.setMethod(propertyName);
		}
		
		return propertyName;
	}
	
	private String getMethodNameFromParamName(String paramName) {
		StringBuilder methodNameBuilder = new StringBuilder(10);
		methodNameBuilder.append("set");
		
		HornPropertyParser parser = HornConfig.getConfig().getPropertyParser(config.getChannel());
		methodNameBuilder.append(parser.getParamMethodName(paramName));
		
		return methodNameBuilder.toString();
	}

	private Object getParamValue(Map<String, Object> param, RequestParam paramConfig) {
		String key = paramConfig.getMapping();
		if (key == null) {
			key = paramConfig.getName();
		}
		
		Object paramValue = param.get(key);
		if (paramValue == null ) {
			paramValue = paramConfig.getDefaultValue();
		}
		
		if (paramConfig.getType().equals(ParamType.STRUCTFIELDS) && paramValue == null) {
			return getStructFields(paramConfig.getStruct());
			
		} else {
			return ParamUtil.formateParam(paramValue, paramConfig);
			
		}
	}
	
	private String getStructFields(String struct) {
		StructConfig config = StructContainer.getContainer().getStruct(struct);
		if (config == null) {
			return null;
		}
		
		String seperator = "";
		StringBuilder fieldsBuilder = new StringBuilder(10);
		for (SourceField field : config.getSource().getFields()) {
			fieldsBuilder.append(seperator);
			fieldsBuilder.append(field.getName());
			
			seperator=",";
		}
		
		log.debug("struct fields " + fieldsBuilder.toString());
		return fieldsBuilder.toString();
	}
}